﻿/**
 * @!ngdoc module
 * @name calendar.services
 * @description
 *   Calendar
 *   日程模块相关服务
 */
angular.module('calendar.services', [])

/**
 * @ngdoc service
 * @name calendar.services.Calendar
 * @description
 * 日程模块的一些数据处理函数，数据持续化功能
 */
.factory('Calendar', function($http, $filter, $ionicLoading, $cacheFactory, Settings, CalendarUtil){

    var getModuleUrl = function () {
      return Settings.rootUrl + "/calendar";
    }
    var cache = $cacheFactory('calendars');
    var dateFilter = $filter('date');

    /**
     * @ngdoc method
     * @name      calendar.services.Calendar#fetch
     * @methodOf  calendar.services.Calendar
     * @description
     *   获取日程列表数据，目前 fetch 获取的是由本月和下月日程的数据
     * @returns {object}
     */
    function fetch(params){

      $ionicLoading.show();

      var httpPromise = $http.get(getModuleUrl(), {
          params: params,
          transformResponse: function(){
            var data =  $http.defaults.transformResponse[0].apply(null, arguments);
            data.events = CalendarUtil.parseEvents(data.events);
            return data;
          }
        })
        .success(function(data){
          // if(data.events){
          //   cache.put('events', data.events);
          // }
        })
        .error(Settings.requestError)
        .success($ionicLoading.hide)
        .error($ionicLoading.hide)

      return httpPromise;
    }

    /**
     * @ngdoc method
     * @name      calendar.services.Calendar#get
     * @methodOf  calendar.services.Calendar
     * @description
     *   获取指定 ID 的日程数据
     * @param {String|Number} id         日程 ID
     * @param  {Boolean} fromCache       是否优先从缓存集合中读取
     * @returns {object}                 日程数据
     */  
    function get(id, fromCache){
      var ret;
      // if(fromCache){

      //   return;
      // } 
      
      $ionicLoading.show();
      return $http.get(getModuleUrl() + '/show', {
        params: { id: id },
        transformResponse: function(){
          var data =  $http.defaults.transformResponse[0].apply(null, arguments);
          return CalendarUtil.parseEvent(data);
        }
      })
      .error(Settings.requestError)
      .success($ionicLoading.hide)
      .error($ionicLoading.hide)
    }
    
    /**
     * @ngdoc method
     * @name      calendar.services.Calendar#formatSubmitData
     * @methodOf  calendar.services.Calendar
     * @description
     *   格式化表单数据为后端可用的数据格式
     *   由于后端代码比较老旧，其命名规则及数据格式都不符合现在的规范
     *   在重构之前需要使用此函数做转化
     *
     * @param {object} data 表单数据
     * @returns {object}
     */
    function formatSubmitData(data){

      // 全天日程时，不计算时分
      if(data.isalldayevent == 1) {
        data.startTime = data.endTime = "00:00";
      // 非全天日程时，结束日期与开始日期相同
      } else {
        data.endDate = data.startDate;
      }

      var startDatetime = CalendarUtil.formatDate(data.startDate) + ' ' + CalendarUtil.formatTime(data.startTime);
      var endDatetime =  CalendarUtil.formatDate(data.endDate) + ' ' + CalendarUtil.formatTime(data.endTime);

      return {
        calendarId:          data.calendarid,
        CalendarTitle:       data.subject,
        Subject:             data.subject,
        Category:            data.category,
        IsAllDayEvent:       +data.isalldayevent,
        CalendarStartTime:   startDatetime,
        CalendarStartTimeed: startDatetime,
        CalendarEndTime:     endDatetime,
        CalendarEndTimeed:   endDatetime
      }
    }

    /**
     * @ngdoc method
     * @name      calendar.services.Calendar#create
     * @methodOf  calendar.services.Calendar
     * @description
     *   创建一条日程并同步到持续层
     * @param {object} data 日程相关数据，[必要字段包括CalendarTitle, Category, IsAllDayEvent, CalendarStartTime, CalendarEndTime]
     * @returns {object} $http post 返回的 promise 对象
     */
    function create(data){
      return $http.post(getModuleUrl() + '/add', data)
      .error(Settings.requestError)
    }

    function update(data){
      return $http.post(getModuleUrl() + '/edit', data)
      .error(Settings.requestError)
    }

    function finish(calendar){
      return $http.post(getModuleUrl() + '/edit&op=finish', calendar);
    }

    function unfinish(calendar){
      return $http.post(getModuleUrl() + '/edit&op=nofinish', calendar);
    }

    function removeOne(id){
      return $http.post(getModuleUrl() + '/del', {
          calendarId: id,
          doption: 'this'
        }
      )
      .error(Settings.requestError)
    }

    return {
      fetch: fetch,
      get: get,
      create: create,
      update: update,
      formatSubmitData: formatSubmitData,
      finish: finish,
      unfinish: unfinish,
      removeOne: removeOne,
      cache: cache
    }
})

/**
 * @ngdoc service
 * @module calendar.services.CalendarUtil
 * @description
 *   日程相关工具函数
 */
.factory('CalendarUtil', function($filter, DATE_FORMAT, TIME_FORMAT, DAY_MILLISECOND){
    // 日程数据输入的时间格式形如/Date(1409803200000)/，需要匹配出正确的时间戳
    var dateReg = /\/Date\((\d+)\)\//;
    var dateFilter = $filter('date');

    function parseDateStr(dateStr){
      var results = dateReg.exec(dateStr);
      return results ? +results[1] : 0;
    }

    /**
     * @ngdoc method
     * @name      calendar.services.CalendarUtil#parseEvents
     * @methodOf  calendar.services.CalendarUtil
     * @description
     *   格式化日程列表数据
     *   目前后端返回日程数据时，列表的数据跟单条的数据格式差异太大
     *   为避难混乱，以单条返回时的数据格式为准
     *   {
     *     calendarid:    "9916",
     *     subject:       "十六夜"
     *     isalldayevent: "1",
     *     starttime:     "1409500800000",
     *     endtime:       "1409500800000",
     *     category:      "4",
     *     status:        "0",
     *     editable:      1
     *   }
     * @param {array} cEvts  原始日程数据
     * @returns {array}
     */
    function parseEvents(cEvts){
      var ret = [];
      // 按日期分组，插入表示日期的数据
      angular.forEach(cEvts, function(cEvt) {

        // 由于周期性日程 id 具有特殊性，需要去除前11位字符
        ret.push({
          calendarid:     cEvt.type == '1' ? cEvt.id.substr(11) : cEvt.id,
          // type == '1' 为周期性日程, '0' 为其他日程
          instancetype:   cEvt.type,
          subject:        cEvt.title,
          isalldayevent:  cEvt.allDay,
          starttime:      parseDateStr(cEvt.start),
          endtime:        parseDateStr(cEvt.end),
          category:       cEvt.category,
          status:         cEvt.status,
          editable:       cEvt.editable
        })
      });

      return ret;
    }

    /**
     * @ngdoc method
     * @name      calendar.services.CalendarUtil#parseEvent
     * @methodOf  calendar.services.CalendarUtil
     * @description
     *   格式化单条日程数据
     *   主要处理时间戳不一致的问题
     * @param {array} cEvt  原始日程数据
     * @returns {array}
     */
    function parseEvent(cEvt){
      return angular.extend(cEvt, {
        starttime: cEvt.starttime ? +cEvt.starttime * 1000 : 0,
        endtime: cEvt.endtime ? +cEvt.endtime * 1000: 0
      });
    }

    function dayFormToday(date){
      var now = new Date;
        // 获取今天 0 时的 date 对象
      var today = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0);

      var timeOffset = +date - +today;

      return Math.floor(timeOffset / DAY_MILLISECOND);
    }

    // 将日程数据按日期排序并分组
    function groupEvents(cEvts){
      if(!cEvts || !cEvts.length) {
        return cEvts;
      }

      var results = [];
      var dateField = '';

      // 排序，按开始时间倒序 
      cEvts = cEvts.sort(function(a, b){
        return b.starttime - a.starttime;
      });

      // 按日期分组，插入表示日期的数据
      angular.forEach(cEvts, function(cEvt) {

        var field = dateFilter(cEvt.starttime, DATE_FORMAT);

        if(field !== dateField) {
          results.push({
            isDateField: true,
            dateField: field,
            startTime: cEvt.starttime,
            dayFormToday: dayFormToday(cEvt.starttime)
          });
          dateField = field;
        }

        results.push(cEvt);
      });

      return results;
    }

    /**
     * @ngdoc method
     * @name      calendar.services.CalendarUtil#formatDate
     * @methodOf  calendar.services.CalendarUtil
     * @description
     *   使用 dateFilter 格式化日期为 yyyy-MM-dd 格式
     *   
     * @param {object|string|number} date  日期对象、时间戳
     * @param {number} dayOffset  与传入日期的天数差
     * @returns {stirng}
     */
    function formatDate(date, dayOffset){
      
      if(!date) {
        return '';
      }

      if(dayOffset) {
        date = +date + dayOffset * DAY_MILLISECOND;
      }

      return dateFilter(date, DATE_FORMAT);
    }

    /**
     * @ngdoc     method
     * @name      calendar.services.CalendarUtil#formatTime
     * @methodOf  calendar.services.CalendarUtil
     * @description
     *   使用 dateFilter 格式化日期为 HH:mm 格式
     *   
     * @param {object} date  日期对象
     * @param {number} minuteOffset  与传入日期的天数差
     * @returns {stirng}
     */
    function formatTime(date, minuteOffset){
      if(!date) {
        return '';
      }

      if(minuteOffset) {
        date = +date + minuteOffset * DAY_MILLISECOND / 1440;
      }

      return dateFilter(date, TIME_FORMAT);
    }

    function parseTime(calendarData){
      var startTime = new Date(calendarData.starttime),
          endTime = new Date(calendarData.endtime);

      return angular.extend({}, calendarData, {
        startDate: startTime,
        endDate: endTime,
        startTime: startTime,
        endTime: endTime
      })
    }

    /**
     * @ngdoc     method
     * @name      calendar.services.CalendarUtil#getDateRange
     * @methodOf  calendar.services.CalendarUtil
     * 
     * @description
     *   日程默认显示本月及下月的安排
     *   此方法获取本月的开始日期和及下月的结束日期
     *   
     * @returns   {Object}   包含 startDate 和 endDate 两个属性 格式为 yyyy-MM-dd
     */
    function getDateRange() {
      var now = new Date();
      var year = now.getFullYear();
      var month = now.getMonth();

      // 计算下月 1 号
      // 考虑跨年的情况
      var startDate = new Date(year, month, 1, 0, 0, 0);
      // 计算下两月 1 号再减一天
      var endDate = new Date(new Date(
        month == 10 ? year + 1: year,
        month == 10 ? 0 : month + 2,
        1, 0, 0, 0
      ) - DAY_MILLISECOND)

      return {
        startDate: dateFilter(startDate, DATE_FORMAT),
        endDate: dateFilter(endDate, DATE_FORMAT)
      }
    }

    
    /**
     * @ngdoc     method
     * @name      calendar.services.CalendarUtil#getCurrentDateTime
     * @methodOf  calendar.services.CalendarUtil
     * @description 
     *   获取当前日期和时分
     * 
     * @returns   {Date}  日期对象
     */
    function getCurrentDateTime() {
      var date = new Date();
      date.setSeconds(0);
      date.setMilliseconds(0);
      return date;
    }

    return {
      parseEvents: parseEvents,
      groupEvents: groupEvents,
      formatDate: formatDate,
      formatTime: formatTime,
      parseTime: parseTime,
      parseEvent: parseEvent,
      getDateRange: getDateRange,
      getCurrentDateTime: getCurrentDateTime
    };
})

/**
 * @ngdoc directive
 * @name calendar.services.directive:calenderColorpicker
 * @element ANY
 * @restrict E
 * @function
 * 
 * @description
 *    日程颜色选择器
 *    日程的选色器没有通用性，所以这里为减少复杂度不提供对外接口
 */
.directive('calenderColorpicker', function($ionicModal) {

  var modalTemplate = '<ion-modal-view>' +
    '<ion-header-bar>' +
      '<button type="button" class="button button-icon icon" ng-click="hideColorPicker()">取消</button>' +
      '<h1 class="title">选择日程颜色</h1>' +
    '</ion-header-bar>' +
    '<ion-content>' +
      '<ion-list>' +
        '<ion-item ng-repeat="color in colors" ng-click="selectColor($index)" style="background-color: {{color}}">&nbsp;</ion-item>' +
      '</ion-list>' +
      '</ion-content>' +
  '</ion-modal-view>';

  return {
    restrict: 'E',
    replace: true,
    require: '?ngModel',
    scope: true,
    // transclude: true,
    
    template:
      '<div class="calendar-colorpicker-toggle">' +
          '<input type="hidden">' +
      '</div>',

    compile: function(element, attr) {
      // 过继各种有效属性到内部的 input
      var input = element.find('input');

      angular.forEach({
        'name': attr.name,
        'ng-value': attr.ngValue,
        'ng-model': attr.ngModel,
        'ng-disabled': attr.ngDisabled,
        'ng-change': attr.ngChange
      }, function(value, name) {
        if (angular.isDefined(value)) {
          input.attr(name, value);
        }
      });

      // Link
      return function($scope, elem, attrs){
        var modal = $ionicModal.fromTemplate(modalTemplate, {
          scope: $scope,
          animation: 'slide-in-up'
        });

        var ngModelController = angular.element(input).controller('ngModel');

        elem.on('click', function(){
          modal.show();
        });

        $scope.colors = ['#3497DB', '#A6C82F', '#F4C73B', '#EE8C0C', '#E76F6F', '#AD85CC', '#98B2D1', '#82939E'];

        $scope.colorpickerModal = modal;

        $scope.showColorPicker = function() {
          $scope.colorpickerModal.show();
        };

        $scope.hideColorPicker = function() {
          $scope.colorpickerModal.hide();
        };

        $scope.$on('$destroy', function() {
          $scope.colorpickerModal.remove();
        });

        $scope.selectColor = function(index) {
          ngModelController.$setViewValue(index);
          $scope.hideColorPicker();
        }
      }
    }
  };
})

/**
 * @ngdoc directive
 * @name calendar.services.directive:currentDate
 * @element ANY
 * @restrict A
 * @function
 * 
 * @description
 *    滚动至当前日期
 */
.directive('currentDate', function($ionicPosition, $ionicScrollDelegate){

  return {
    restrict: 'A',

    compile: function(element, attr) {
      // Link
      return function($scope, elem, attrs){
        $ionicScrollDelegate.scrollTo(0, $ionicPosition.position(elem).top || 0)
      }
    }
  };
})
